using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LambdaGp.Core
{
    public class Individual : IComparable
    {
        private Func<double> _func;
        public Individual()
        {
            Storage = new List<IStorable>();
        }

        public OperatorNode OperatorTree { get; set; }
        public double? Fitness { get; set; }
        public double? FitnessBeforeParsimony { get; set; }
        public int Size { get { return OperatorTree.Size; } }
        public int Depth { get { return OperatorTree.Depth; } }
        public List<IStorable> Storage { get; set; }
        public object Data { get; set; }

        public Individual Clone()
        {
            Individual clone = new Individual();
            clone.Fitness = Fitness;
            clone.OperatorTree = OperatorTree.Clone();
            clone._func = null;
            clone.Storage.AddRange(Storage.Select(var => var.Clone()));
            return clone;
        }

        public override string ToString()
        {
            return OperatorTree.ToString();
        }

        public string ToPrettyString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            OperatorTree.AddPrettyString(stringBuilder, string.Empty);
            return stringBuilder.ToString();
        }

        public string ToCompactString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            OperatorTree.AddCompactString(stringBuilder);
            return stringBuilder.ToString();
        }

        public int CompareTo(object obj)
        {
            Individual other = (Individual)obj;
            return Fitness.Value.CompareTo(other.Fitness.Value);
        }

        public double Evaluate()
        {
            if (_func == null)
            {
                _func = OperatorTree.CompileToFunc(this);
            }

            return _func();
        }

        public void Finalize(GenomeDefinition genomeDefinition)
        {
            Storage.Clear();
            foreach (Operator @operator in genomeDefinition.Operators)
            {
                @operator.AddToStorage(this);
            }
        }
    }
}
